package com.qf.security1.filters;

import com.alibaba.fastjson.JSON;
import com.qf.security1.domain.Code;
import com.qf.security1.domain.R;
import com.qf.security1.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;

public class VerifyTokenFilter extends BasicAuthenticationFilter {
    public VerifyTokenFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //验证jwt的合法性: 合法--登录状态，不合法--未登录状态
        String token = request.getHeader("token");

        if (StringUtils.isEmpty(token)){
            //没有携带token
            chain.doFilter(request,response);
            return;
        }

        /*
            从redis中获取到黑名单列表，先判断当前token是否在黑名单列表中
                ① 在，直接返回重新登录
                ② 不在，执行正常解析逻辑
         */

        try {
            //解析token
            Claims claims = JwtUtils.parseBody(token);

            setContext(claims);

            chain.doFilter(request,response);
        }catch (ExpiredJwtException expireEx){
            //token过期了,操作过程中的过期，要允许复活(过期时间在1个小时内，允许复活)
            Claims claims = expireEx.getClaims();
            String username = claims.getSubject();
            //获取token过期时间毫秒值
            long expiration = claims.getExpiration().getTime();
            if (expiration + 1000*60*60 <= System.currentTimeMillis()){
                //复活--向浏览器生成一个新的token（刷新token）
                String newToken = JwtUtils.createToken(claims, username);
                response.setHeader("refresh_token",newToken);

                setContext(claims);

                chain.doFilter(request,response);
            }else{
                //过期了重新登录
                R r = R.error("token过期了", Code.NOT_LOGIN);
                response.setContentType("application/json;charset=utf-8");
                String jsonStr = JSON.toJSONString(r);

                response.getWriter().write(jsonStr);
            }

        }catch (Exception e){
            //token非法 -- 重新登录
            R r = R.error("token非法", Code.NOT_LOGIN);
            response.setContentType("application/json;charset=utf-8");
            String jsonStr = JSON.toJSONString(r);

            response.getWriter().write(jsonStr);
        }
    }

    private void setContext(Claims claims) {
        //获取用户名
        String username = claims.get("username", String.class);
        List<String> roles = claims.get("roles", List.class);

        //在security中表示登录的方式是，向Security的上下文中塞入一个认证对象
        List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(roles.toArray(new String[0]));
        //构建一个认证对象
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, authorityList);
        //将认证对象设置到security的上下文中（表示登录了）
        SecurityContextHolder.getContext().setAuthentication(auth);
    }
}
